package dawn.jxufe.community.controller;

import com.google.code.kaptcha.Producer;
import dawn.jxufe.community.entity.User;
import dawn.jxufe.community.service.UserService;
import dawn.jxufe.community.util.CommunityConstant;
import dawn.jxufe.community.util.CommunityUtil;
import dawn.jxufe.community.util.RedisKeyUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpRequest;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author dawn21
 * @version 1.0
 * @date 2022/7/10 21:33
 * @component
 */

// 实现接口才好使用里面的常量。

@Controller
public class LoginController implements CommunityConstant {

    // 创建日志
    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private UserService userService;

    // 注入Producer的bean，才可以使用，然后生成验证码。
    @Autowired
    private Producer kaptchaProducer;

    // 注入项目路径。
    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Autowired
    private RedisTemplate redisTemplate;

    //点击主页面注册按钮，返回注册页面。
    @RequestMapping(path = "/register",method = RequestMethod.GET)
    public String getRegisterPage(){
        return "/site/register";
    }

    //点击邮箱中的激活，成功后，返回到登录页面。
    @RequestMapping(path = "/login",method = RequestMethod.GET)
    public String getLoginPage(){
        return "/site/login";
    }

    //点击主页面必看按钮，返回必看页面。
    @RequestMapping(path = "/readme",method = RequestMethod.GET)
    public String getReadMePage(){
        return "/site/readme";
    }

    //点击注册页面注册按钮，执行业务逻辑。
    @RequestMapping(path="/register", method = RequestMethod.POST)
    public String register(Model model,
                           @Param("username") String username,
                           @Param("password") String password,
                           @Param("email") String email
                           ){
        User user = new User();
        user.setName(username);
        user.setPassword(password);
        user.setEmail(email);
        Map<String , Object> map = userService.register(user);
        //用户输入无误
        if (map == null || map.isEmpty()) {
            model.addAttribute("msg",
                    "注册成功，我们已经向您的邮箱发送了一封激活邮件，请尽快激活！。");
            //注册成功后8秒后自动跳到的页面就是首页。
            model.addAttribute("target","/index");
            //这个是提示注册成功的页面，8秒后会跳到首页。
            //返回操作结果页面，就不是主页面。
            return "site/operate-result";
        }else {
            //用户输入有误，返回注册页面。
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            model.addAttribute("emailMsg", map.get("emailMsg"));
            return "/site/register";
        }
    }

    // http://localhost:8080/community/activation/101/code
    /** 思路：用户输入信息点击注册之后，就会跳到register，就会执行程序，
     * 上面的register执行userService就会发送一封邮件，userService中有url( /activation/{userId}/{code} )，
     * 发送一封邮件给邮箱，
     * activation.html是邮件(载体)，
     * 点击邮件中的链接就会访问这个controller(映射地址)，就可以注册成功，
     * 最后返回到site/operate-result.html
     * site/operate-result.html会自动跳到首页，注册过程半结束。接下来就是验证码过程。
     */
    @RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)
    public String activation(Model model,
                             @PathVariable("userId") int userId,
                             @PathVariable("code") String code){
        int result = userService.activation(userId, code);//返回状态结果。
        if (result == ACTIVATION_SUCCESS) {
            //成功
            model.addAttribute("msg", "激活成功，您的账号已经可以正常使用了！。");
            model.addAttribute("target","/login");
        }else if (result == ACTIVATION_REPEAT ) {
            //重复
            model.addAttribute("msg", "无效操作，该账号已经激活过了！。");
            model.addAttribute("target","/index");
        }else {
            //失败
            model.addAttribute("msg", "激活失败，您提供的激活码不正确！。");
            model.addAttribute("target","/index");
        }
        return "site/operate-result";
    }

    // 返回验证码照片
    @RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
    public void getKaptcha(HttpServletResponse response/*, HttpSession session*/){
        // 生成验证码字符串
        String text = kaptchaProducer.createText();
        // 生成验证码照片
        BufferedImage image =  kaptchaProducer.createImage(text);

        // 验证码字符串存到session，保存在服务器，便于对比验证。
        // session.setAttribute("kaptcha", text);

        // 验证码的归属(一个凭证(字符串))。字符串凭证发给客户端，客户端用cookie保存。
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setMaxAge(60); // 60秒
        cookie.setPath(contextPath); // 有效路径(整个项目)
        response.addCookie(cookie); // 将cookie发给客户端(通过response发送)
        // 验证码字符串存到redis。
        String rediskey = RedisKeyUtil.getKaptchaKey(kaptchaOwner); // 获取验证码的key。
        redisTemplate.opsForValue().set(rediskey, text, 180 , TimeUnit.SECONDS);


        // 验证码照片发给浏览器显示。
        // 声明response返回的是图片。
        response.setContentType("image/png");
        // 获取response的字节输出流。
        try {
            OutputStream os = response.getOutputStream();
            // 使用工具生成图片，image给os。
            ImageIO.write(image, "png", os);

        } catch (IOException e) {
            logger.error("响应验证码失败：" + e.getMessage());
        }
    }

    // 前端调用这个映射地址。两个login，只要请求方式 不一样就不会冲突。
    @RequestMapping(path = "/login", method = RequestMethod.POST)
    // 用户名、密码、验证码、是否记住我(是的话就保存时间长一点)
    // 从session中取出验证码用于和用户输入的验证码对比。response用于保存cookie，cookie用户保存凭证。
    public String login(String username, String password, String code, boolean rememberme,
                        Model model, /*HttpSession session, */HttpServletResponse response,
                        @CookieValue("kaptchaOwner") String kaptchaOwner){

        // 验证码只在表现层判断，因为不需要访问数据库，所以不需要业务逻辑层。所以先判断验证码。
        // 获取验证码。
        // String kaptcha = (String) session.getAttribute("kaptcha");
        String kaptcha =  null; // cookie中取kaptchaOwner(是一个redis的key) -> 取redis的值(一个验证码字符串)
        if (StringUtils.isNotBlank(kaptchaOwner)) { // 如果key不为空。
            String rediskey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
            kaptcha = (String) redisTemplate.opsForValue().get(rediskey);
        }
        // session中验证码为空、用户输入的验证码为空、忽略大小写(验证码不一致)
        if(StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
            model.addAttribute("codeMsg", "验证码错误！");
            // 返回到登录页面。
            return "/site/login";
        }

        // 验证码无误，检查账号、密码。
        int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
        Map<String, Object> map = userService.login(username, password, expiredSeconds);
        // 如果只有ticket，那么就是成功了。
        if (map.containsKey("ticket")) {
            // 成功：将凭证(ticket)名字发给客户端，让客户端存贮。
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
            // 设置cookie有效的路径。application.properties中设置过一个，在前面注入就好，然后这里使用。
            cookie.setPath(contextPath);
            // 设置cookie有效时间
            cookie.setMaxAge(expiredSeconds);
            // 在响应中加入cookie，便于发送给页面。
            response.addCookie(cookie);
            // 重定向到首页。
            return "redirect:/index";
        }else {
            // 账号或密码有问题。
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            // 返回到登录页面。
            return "/site/login";
        }
    }

    // 退出登录，其实就是把凭证状态改为1.
    @RequestMapping(path = "logout", method = RequestMethod.GET)
    // 浏览器已经有cookie了，那么就要拿到cookie中的ticket来修改状态。
    // @CookieValue("ticket")
    public String logout(@CookieValue(value = "ticket") String ticket) {
        userService.logout(ticket);
        SecurityContextHolder.clearContext();
        return "redirect:/login";
    }

}
